package com.heima.crm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.heima.crm.build.IndexDataBuilder;
import com.heima.crm.common.contents.CommonMessageContent;
import com.heima.crm.common.result.PageResult;
import com.heima.crm.common.result.Result;
import com.heima.crm.common.utils.BaseContext;
import com.heima.crm.mapper.*;
import com.heima.crm.pojos.dtos.*;
import com.heima.crm.pojos.entity.*;
import com.heima.crm.pojos.vos.*;
import com.heima.crm.service.StatisticsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.*;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * 统计分析相关接口
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class StatisticsServiceImpl implements StatisticsService {
    private final ClueMapper clueMapper;
    private final TbBusinessMapper businessMapper;
    private final TbCustomerMapper customerMapper;
    private final ContractMapper contractMapper;
    private final CrUserMapper userMapper;
    private final StatisticsMapper statisticsMapper;
    private final TbCourseMapper courseMapper;
    private final SysDicDataMapper dicDataMapper;

    /**
     * 查询首页数据
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public Result getIndexData(LocalDate begin, LocalDate end) {
        //参数校验
        if (begin == null || end == null) {
            return Result.error(CommonMessageContent.PARAMETER_EXCEPTION);
        }
        //时间转化
        Date beginDate = Date.from(begin.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date endDate = Date.from(end.atStartOfDay(ZoneId.systemDefault()).toInstant());
        Date beginTime = getStartOfDay(beginDate);
        Date endTime = getEndOfDay(endDate);
        //获取登录用户
        Long userId = BaseContext.getCurrentId();
        if (userId == null) {
            return Result.error(CommonMessageContent.NEED_LOGIN);
        }
        TbUser user = userMapper.selectById(userId);
        if (user == null) {
            return Result.error(CommonMessageContent.USER_DOES_NOT_EXIST);
        }
        //构建首页返回值对象
        IndexDataBuilder indexDataBuilder = new IndexDataBuilder(beginTime, endTime,
                clueMapper, businessMapper, customerMapper, contractMapper, user,
                statisticsMapper);
        IndexVo vo = indexDataBuilder.buildBasicData()
                .buildTodayData()
                .buildToDoData()
                .buildFunnelData()
                .buildBoardData()
                .build();
        return Result.success(vo);
    }

    /**
     * 客户统计图表
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public Result cusChart(LocalDate begin, LocalDate end) {
        CusChartVo vo = new CusChartVo();
        //1.封装日期集合数据
        ArrayList<LocalDate> dateList = new ArrayList<>();
        LocalDate date = begin;
        dateList.add(date);
        while (!date.equals(end)) {
            date = date.plusDays(1);
            dateList.add(date);
        }
        vo.setDateList(StringUtils.join(dateList, ","));
        //newCustomerList
        //totalCustomerList
        ArrayList<Integer> cusList = new ArrayList<>();
        ArrayList<String> totalList = new ArrayList<>();
        for (LocalDate localDate : dateList) {
            LocalDateTime minDayTime = LocalDateTime.of(localDate, LocalTime.MIN);
            LocalDateTime maxDayTime = LocalDateTime.of(localDate, LocalTime.MAX);
            Integer cusCount = Math.toIntExact(customerMapper.selectCount(new LambdaQueryWrapper<TbCustomer>()
                    .ge(TbCustomer::getCreateTime, minDayTime)
                    .lt(TbCustomer::getCreateTime, maxDayTime)));
            Integer totalCount = Math.toIntExact(customerMapper.selectCount(new LambdaQueryWrapper<TbCustomer>()
                    .lt(TbCustomer::getCreateTime, maxDayTime)));
            cusList.add(cusCount);
            String s = new DecimalFormat("#.0").format(totalCount * 1.0 / 10000);
            totalList.add(s);
        }
        String newCustomerList = StringUtils.join(cusList, ",");
        vo.setNewCustomerList(newCustomerList);
        String totalCustomerList = StringUtils.join(totalList, ",");
        vo.setTotalCustomerList(totalCustomerList);
        //subjectList
        //percentList
        ArrayList<String> subList = new ArrayList<>();
        ArrayList<String> perList = new ArrayList<>();
        Integer total = Math.toIntExact(contractMapper.selectCount(new LambdaQueryWrapper<TbContract>()
                .ge(TbContract::getCreateTime, begin)
                .lt(TbContract::getCreateTime, getEndOfDay(Date.from(end.atStartOfDay(ZoneId.systemDefault()).toInstant())))));
        List<SubCountDto> dtos = statisticsMapper.getSubList(begin, end);
        dtos.forEach(dto -> {
            subList.add(dto.getSub());
            String percent = BigDecimal.valueOf(dto.getCount())
                    .divide(BigDecimal.valueOf(total),
                            4, BigDecimal.ROUND_HALF_UP).toString();
            perList.add(percent);
        });
        vo.setSubjectList(StringUtils.join(subList, ","));
        vo.setPercentList(StringUtils.join(perList, ","));
        return Result.success(vo);
    }

    /**
     * 客户统计报表
     *
     * @param dto
     * @return
     */
    @Override
    public Result cusPage(CustomerStatisticsQueryDto dto) {

        ArrayList<CusPageVo> vos = new ArrayList<>();
        //mybatis pageHelper写法，注意导入的依赖包
        PageHelper.startPage(dto.getPage(), dto.getSize());
        // TODO 表结构不完整，mapper待实现
        Page<TbCustomer> page = statisticsMapper.selectByCondition(dto);
        List<TbCustomer> r = page.getResult();
        for (TbCustomer customer : r) {
            CusPageVo vo = new CusPageVo();
            vo.setId(customer.getId());
            vo.setCustomerName(customer.getCustomerName());
            TbContract contract = contractMapper.selectOne(new LambdaQueryWrapper<TbContract>()
                    .eq(TbContract::getCustomName, customer.getCustomerName()));
            if (contract != null) {
                TbCourse course = courseMapper.selectById(contract.getCourseId());
                vo.setName(course.getName());
                SysDictData courseSubject = dicDataMapper.selectOne(new LambdaQueryWrapper<SysDictData>()
                        .eq(SysDictData::getDictType, "course_subject")
                        .eq(SysDictData::getDictValue, contract.getSubjectId()));
            } else {
                vo.setName(null);
                vo.setSubject(null);
            }
            vo.setChannel(customer.getChannel());
            vo.setCreateTime(customer.getCreateTime().toString());
            TbBusiness business = businessMapper.selectOne(new LambdaQueryWrapper<TbBusiness>()
                    .eq(TbBusiness::getId, customer.getBusinessId()));
            if (business != null) {
                vo.setAscriptionName(business.getAscriptionName());
            } else {
                vo.setAscriptionName(null);
            }
            vos.add(vo);
        }
        return Result.success(new PageResult(page.getTotal(), vos));
    }

    /**
     * 销售统计图表
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public Result sellChart(LocalDate begin, LocalDate end) {
        SellChartVo vo = new SellChartVo();
        //1.封装日期集合数据
        ArrayList<LocalDate> dateList = new ArrayList<>();
        LocalDate date = begin;
        dateList.add(date);
        while (!date.equals(end)) {
            date = date.plusDays(1);
            dateList.add(date);
        }
        vo.setDateList(StringUtils.join(dateList, ","));
        List<Long> list = new ArrayList<>();
        for (LocalDate localDate : dateList) {
            LocalDateTime minDayTime = LocalDateTime.of(localDate, LocalTime.MIN);
            LocalDateTime maxDayTime = LocalDateTime.of(localDate, LocalTime.MAX);
            List<TbContract> tbContracts = contractMapper.selectList(new LambdaQueryWrapper<TbContract>()
                    .ge(TbContract::getCreateTime, minDayTime)
                    .le(TbContract::getCreateTime, maxDayTime));
            Long count = tbContracts.stream().mapToDouble(TbContract::getCount).count();
            list.add(count);
        }
        vo.setSaleList(StringUtils.join(list, ","));
        return Result.success(vo);
    }

    /**
     * 销售统计报表
     *
     * @param dto
     * @return
     */
    @Override
    public Result sellPage(SellStatisticsQueryDto dto) {
        PageHelper.startPage(dto.getPage(), dto.getSize());
        // TODO 表结构不完整，mapper待实现
        Page<SellPageVo> page = statisticsMapper.getSellPage(dto);
        return Result.success(new PageResult(page.getTotal(), page.getResult()));
    }

    /**
     * 线索统计图表
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public Result clueChart(LocalDate begin, LocalDate end) {
        ClueChartVo vo = new ClueChartVo();
        //1.封装日期集合数据
        ArrayList<LocalDate> dateList = new ArrayList<>();
        LocalDate date = begin;
        dateList.add(date);
        while (!date.equals(end)) {
            date = date.plusDays(1);
            dateList.add(date);
        }
        vo.setDateList(StringUtils.join(dateList, ","));
        List<Integer> dayList = new ArrayList<>();
        List<Integer> totalList = new ArrayList<>();
        for (LocalDate localDate : dateList) {
            LocalDateTime minDayTime = LocalDateTime.of(localDate, LocalTime.MIN);
            LocalDateTime maxDayTime = LocalDateTime.of(localDate, LocalTime.MAX);
            Integer dayCount = Math.toIntExact(clueMapper.selectCount(new LambdaQueryWrapper<TbClue>()
                    .ge(TbClue::getCreateTime, minDayTime)
                    .le(TbClue::getCreateTime, maxDayTime)));
            Integer totalCount = Math.toIntExact(clueMapper.selectCount(new LambdaQueryWrapper<TbClue>()
                    .le(TbClue::getCreateTime, maxDayTime)));
            dayList.add(dayCount);
            totalList.add(totalCount);
        }
        vo.setNewClueList(StringUtils.join(dayList, ","));
        vo.setTotalClueList(StringUtils.join(totalList, ","));
        List<Integer> clueList = new ArrayList<>();
        Integer totalClue = Math.toIntExact(clueMapper.selectCount(new LambdaQueryWrapper<TbClue>()
                .ge(TbClue::getCreateTime, begin)
                .le(TbClue::getCreateTime, end)));
        Integer validClue = Math.toIntExact(clueMapper.selectCount(new LambdaQueryWrapper<TbClue>()
                .ge(TbClue::getCreateTime, begin)
                .le(TbClue::getCreateTime, end)
                .ne(TbClue::getStatus, 4)));
        Integer businessCount = Math.toIntExact(businessMapper.selectCount(new LambdaQueryWrapper<TbBusiness>()
                .ge(TbBusiness::getCreateTime, begin)
                .le(TbBusiness::getCreateTime, end)));
        Integer cusCount = Math.toIntExact(customerMapper.selectCount(new LambdaQueryWrapper<TbCustomer>()
                .ge(TbCustomer::getCreateTime, begin)
                .le(TbCustomer::getCreateTime, end)));
        clueList.add(totalClue);
        clueList.add(validClue);
        clueList.add(businessCount);
        clueList.add(cusCount);
        vo.setSortCountList(StringUtils.join(clueList, ","));
        //计算百分比
        String validRate = BigDecimal.valueOf(validClue)
                .divide(BigDecimal.valueOf(totalClue),
                        4, BigDecimal.ROUND_HALF_UP).toString();
        String businessRate = BigDecimal.valueOf(businessCount)
                .divide(BigDecimal.valueOf(totalClue),
                        4, BigDecimal.ROUND_HALF_UP).toString();
        String cusRate = BigDecimal.valueOf(cusCount)
                .divide(BigDecimal.valueOf(totalClue),
                        4, BigDecimal.ROUND_HALF_UP).toString();
        List<String> rateList = new ArrayList<>();
        rateList.add("1");
        rateList.add(validRate);
        rateList.add(businessRate);
        rateList.add(cusRate);
        vo.setPercentClueList(StringUtils.join(rateList, ","));
        return Result.success(vo);
    }

    /**
     * 线索统计报表
     *
     * @param dto
     * @return
     */
    @Override
    public Result cluePage(ClueStatisticsQueryDto dto) {
        PageHelper.startPage(dto.getPage(), dto.getSize());
        // TODO sql待完善
        Page<CluePageVo> page = statisticsMapper.getCluePage(dto);
        return Result.success(new PageResult(page.getTotal(), page.getResult()));
    }

    /**
     * 渠道统计图表
     *
     * @param begin
     * @param end
     * @return
     */
    @Override
    public Result channelChart(LocalDate begin, LocalDate end) {
        // TODO mock数据
        List<ChannelChartVo> vos = new ArrayList<>();
        vos.add(new ChannelChartVo("线上活动", "0.5714",
                "情人节优惠,中秋特惠,端午回馈,欢乐五一",
                "0.3000,0.3000,0.3000,0.1000", 2000));
        vos.add(new ChannelChartVo("推荐介绍", "0.2857",
                "情人节优惠,中秋特惠,端午回馈,欢乐五一",
                "0.3000,0.3000,0.3000,0.1000", 1000));
        vos.add(new ChannelChartVo("其他自定义", "0.1429",
                "情人节优惠,中秋特惠,端午回馈,欢乐五一",
                "0.3000,0.3000,0.3000,0.1000", 5000));
        return Result.success(vos);
    }

    /**
     * 渠道统计报表
     *
     * @param dto
     * @return
     */
    @Override
    public Result channelPage(ChannelStatisticsQueryDto dto) {
        PageHelper.startPage(dto.getPage(), dto.getSize());
        Page<ChannelPageVo> page = statisticsMapper.getChannelPage(dto);
        return Result.success(new PageResult<>(page.getTotal(), page.getResult()));
    }


    // 获得某天最大时间 2020-02-19 23:59:59
    public static Date getEndOfDay(Date date) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
        LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
        return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
    }

    // 获得某天最小时间 2020-02-17 00:00:00
    public static Date getStartOfDay(Date date) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()), ZoneId.systemDefault());
        LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);
        return Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
    }
}
